/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：呼叫中心-系统通用工具封装层
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taiping.cc.application.tools;

import lombok.experimental.UtilityClass;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@UtilityClass
public final class DateUtils {
// CHECKSTYLE:OFF

    /**
     * 中国大陆标准时区
     */
    public static final ZoneId ZONE = ZoneId.of("Asia/Shanghai");

    /**
     * LocalDateTime类型转换为Date类型
     */
    public static Date parseOf(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
        Instant instant = zonedDateTime.toInstant();
        return Date.from(instant);
    }

    /**
     * Date类型转换为LocalDateTime类型
     */
    public static LocalDateTime parseOf(Date date) {
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDateTime();
    }

    /**
     * 获取月份的第一天表示的日期
     */
    public static LocalDateTime firstDayOfMonth(LocalDateTime localDateTime) {
        return localDateTime.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取月份的最后一天表示的日期
     */
    public static LocalDateTime lastDayOfMonth(LocalDateTime localDateTime) {
        return localDateTime.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 相对于某一个日期，获取昨天所表示的日期
     */
    public static LocalDateTime yesterdayOfMonth(LocalDateTime localDateTime) {
        return localDateTime.plusDays(-1L);
    }

    /**
     * 相对于某一个日期，获取明天所表示的日期
     */
    public static LocalDateTime tomorrowOfMonth(LocalDateTime localDateTime) {
        return localDateTime.plusDays(1L);
    }

    /**
     * 计算两个日期之间的时间间隔
     *
     * @param startDateTime 起始日期
     * @param endDateTime   结束日期
     * @return Returns the time interval in milliseconds.
     */
    public static Long getDateDistance(final Date startDateTime, final Date endDateTime) {
        Long startTimestamp = getTimestamp(DateUtils.parseOf(startDateTime));
        Long endTimestamp = getTimestamp(DateUtils.parseOf(endDateTime));
        return endTimestamp - startTimestamp;
    }

    /**
     * 计算两个日期之间的时间间隔
     *
     * @param startDateTime 起始日期
     * @param endDateTime   结束日期
     * @return Returns the time interval in milliseconds.
     */
    public static Long getDateDistance(final LocalDateTime startDateTime, final LocalDateTime endDateTime) {
        Duration duration = Duration.between(startDateTime, endDateTime);
        return duration.toMillis();
    }

    /**
     * 将时间戳转换为日期类型
     *
     * @param timestamp 以毫秒为单位的时间戳
     * @return 返回日期时间对象
     */
    public static LocalDateTime getLocalDateTime(Long timestamp) {
        Instant instant = Instant.ofEpochMilli(timestamp);
        return LocalDateTime.ofInstant(instant, ZONE);
        /*
        localDateTime.get(ChronoField.YEAR);
        localDateTime.get(ChronoField.MONTH_OF_YEAR);
        localDateTime.get(ChronoField.DAY_OF_MONTH);
        localDateTime.getDayOfYear();
        localDateTime.getHour();
        localDateTime.getMinute();
        localDateTime.getSecond();
         */
    }

    /**
     * 获取时间戳
     *
     * @return Returns the timestamp in milliseconds.
     */
    public static Long getTimestamp(LocalDateTime localDateTime) {
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZONE);
        return zonedDateTime.toInstant().toEpochMilli();
    }

    /**
     * 将以毫秒为单位的数字转换为 N天N小时N分钟N秒N毫秒
     *
     * @param milliSeconds 时间长度，以毫秒为单位
     * @return 返回时间长度的中文说明
     */
    public static String formatTimestamp(final Long milliSeconds) {
        long days = TimeUnit.MILLISECONDS.toDays(milliSeconds);
        long hours = TimeUnit.MILLISECONDS.toHours(milliSeconds) - TimeUnit.DAYS.toHours(days);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(milliSeconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliSeconds));
        long seconds = TimeUnit.MILLISECONDS.toSeconds(milliSeconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliSeconds));
        long ms = TimeUnit.MILLISECONDS.toMillis(milliSeconds) - TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(milliSeconds));
        return String.format("[%s天 %s小时 %s分钟 %s秒 %s毫秒]", days, hours, minutes, seconds, ms);
    }

    /**
     * 指定日期对象加上一个时间段得到新的日期
     *
     * @param date   指定日期
     * @param expire 时长
     * @param field  时间单位，如：小时、分钟、秒
     * @return 返回一个新的日期
     */
    public static Date offset(Date date, int expire, int field) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(field, expire);
        return instance.getTime();
    }

    /**
     * 获取日期的字符串列表
     *
     * @param startDate     开始日期
     * @param endDate       结束日期
     * @param dateFormatter 格式化为日期的字符串形式
     * @return 返回日期的字符串列表
     */
    public static List<String> getDateList(final Date startDate, final Date endDate, Function<Date, String> dateFormatter) {
        List<String> list = new ArrayList<String>();
        if (startDate.getTime() > endDate.getTime()) {
            return list;
        }

        list.add(dateFormatter.apply(startDate));
        if (startDate.getTime() == endDate.getTime()) {
            return list;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        calendar.add(Calendar.DAY_OF_YEAR, 1);

        list.addAll(getDateList(calendar.getTime(), endDate, dateFormatter));
        return list;
    }

    /**
     * 获取日期的字符串列表
     *
     * @param startDate     开始日期
     * @param endDate       结束日期
     * @param dateFormatter 格式化为日期的字符串形式
     * @return 返回日期的字符串列表
     */
    public static List<String> getDateList(final LocalDateTime startDate, final LocalDateTime endDate, Function<Date, String> dateFormatter) {
        return DateUtils.getDateList(DateUtils.parseOf(startDate), DateUtils.parseOf(endDate), dateFormatter);
    }

    /**
     * 获取日期列表
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 返回日期的字符串列表
     */
    public static List<Date> getDateList(final Date startDate, final Date endDate) {
        List<Date> list = new ArrayList<>();
        if (startDate.getTime() > endDate.getTime()) {
            return list;
        }

        list.add(startDate);
        if (startDate.getTime() == endDate.getTime()) {
            return list;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        calendar.add(Calendar.DAY_OF_YEAR, 1);

        list.addAll(getDateList(calendar.getTime(), endDate));
        return list;
    }

    /**
     * 获取指定区间的日期列表
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 返回日期的字符串列表
     */
    public static List<Date> getDateList(final LocalDateTime startDate, final LocalDateTime endDate) {
        return DateUtils.getDateList(DateUtils.parseOf(startDate), DateUtils.parseOf(endDate));
    }

// CHECKSTYLE:ON
}
